package com.sunday.common.netty.e2_Time;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * 编写时间客户端
 * 与服务器不同，我们需要一个客户端来执行该协议，因为人类无法将 32 位二进制数据转换为日历上的日期。
 * 在本节中，我们将讨论如何确保服务器正常工作，并学习如何使用Netty编写客户端。DISCARDECHOTIME
 * Netty 中服务器和客户端之间最大且唯一的区别是使用了不同的 Bootstrap 和 Channel 实现。请看下面的代码：
 */
public class TimeClient {

    public static final String SERVER_HOST = "localhost";
    public static final int SERVER_PORT = 8811;

    public static void main(String[] args) throws Exception {

        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            /**
             * Bootstrap 类似于 ServerBootstrap，只不过它适用于非服务器通道，例如客户端或无连接通道。
             */
            Bootstrap b = new Bootstrap(); // (1)
            /**
             * 如果仅指定一个 EventLoopGroup，则它将同时用作老板组和工作线程组。
             * 不过，boss worker 不用于客户端。
             */
            b.group(workerGroup); // (2)
            /**
             * NioSocketChannel 用于创建客户端通道，而不是 NioServerSocketChannel。
             */
            b.channel(NioSocketChannel.class); // (3)
            /**
             * 请注意，我们在这里不使用，因为我们没有 ，因为客户端 SocketChannel 没有父级。childOption() ServerBootstrap
             */
            b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new TimeDecoder(), new TimeClientHandler());
                }
            });

            /**
             * 我们应该调用方法而不是方法。connect() bind()
             * sync() 等待连接完成。
             */
            // Start the client.
            ChannelFuture f = b.connect(SERVER_HOST, SERVER_PORT).sync(); // (5)

            /**
             * f.channel().closeFuture().sync() 等待客户端链路关闭。
             * 当客户端链路关闭之后，它会被唤醒。
             * 然后调用 f.channel().closeFuture().sync() 等待客户端链路关闭，main函数退出退出。
             * 通过调用 sync() 方法同步等待连接关闭处理完成，这里会阻塞等待通道关闭。
             */
            // Wait until the connection is closed.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }

}
